#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>

typedef struct {
	int		bus_id;
	int 		addr;
	__u8		reg;
	int	 	dir;
	int		size;
	__u8		*data;
}i2crdwr;


int i2c_xfer(int handle, int addr, __u8 reg,int dir, int size, __u8 *rwbuf){
	struct i2c_rdwr_ioctl_data rdwr;
	rdwr.msgs=(struct i2c_msg *)malloc(sizeof(*(rdwr.msgs))*2);
	switch(dir){
	case 'r':
		rdwr.nmsgs=2;
		rdwr.msgs[0].addr=addr;
		rdwr.msgs[0].flags=0;
		rdwr.msgs[0].len=1;
		rdwr.msgs[0].buf=&reg;
	
		rdwr.msgs[1].addr=addr;
		rdwr.msgs[1].flags=I2C_M_RD;
		rdwr.msgs[1].len=size;
		rdwr.msgs[1].buf=rwbuf;	
		break;
	case 'w':
		rdwr.nmsgs=1;
		rdwr.msgs[0].addr=addr;
		rdwr.msgs[0].flags=0;
		rdwr.msgs[0].len=size+1;
		rdwr.msgs[0].buf=rwbuf;
		break;
	default:
		break;
	}
	
	int reval = ioctl(handle, I2C_RDWR, &rdwr);
	if(reval == rdwr.nmsgs){
		free(rdwr.msgs);
		return 0;
	}
	free(rdwr.msgs);
	return -1;
	
}
int i2c_handle(int bus_id, int addr, __u8 reg, int dir, int size, __u8 *rwbuf){
	int handle;

	if(bus_id == 0)
		handle = open("/dev/i2c-0", O_RDWR);
	else if(bus_id == 1)
		handle = open("/dev/i2c-1", O_RDWR);
	else if(bus_id == 2)
		handle = open("/dev/i2c-2", O_RDWR);
	else if(bus_id == 3)
		handle = open("/dev/i2c-3", O_RDWR);
	else if(bus_id == 4)
		handle = open("/dev/i2c-4", O_RDWR);
	else if(bus_id == 5)
		handle = open("/dev/i2c-5", O_RDWR);
	else
		return 1;

	if (handle<0){
		printf("open i2c-%d fail!!!\r\n", bus_id);
		return 1;
	}

	int  reval1 = i2c_xfer(handle,addr,reg,dir,size,rwbuf);
	if (reval1){
		printf("xfer error\n");
		return reval1;
	}
	
	int reval2 = close(handle);
	if (reval2){
		printf("close i2c-%d fail!!!\r\n", bus_id);
		return reval2;
	}
	return 0;		
}

int hextodec(char *str){
	int value;
	const char *cp=str;
	unsigned int result=0;	
	while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp)
		? toupper(*cp) : *cp)-'A'+10) < 16) {
   		 result = result*16 + value;
    		 cp++;
 	 }	
	return result;						
}

void printinfo(){
  printf("\
read data from i2c:\n \
		i2c <bus> <addr> <reg> <direction> <size>\n\
write data from i2c:\n \
		i2c <bus> <addr> <reg> <direction> <size> <data>\n\n\
<bus>		bus_id\n\
<addr>		device address\n\
<reg>		register address\n\
<direction>	write or read\n\
<size>		bytes of data\n\
<data>		data to write\n\
\n");
	 
}

int main(int argc, char *argv[]){	
	if (argc < 5) {
		printinfo();
	        return;
	  } 

	char **arg=argv;
	i2crdwr i2c_rw={
		.bus_id	= atoi(arg[1]),
		.addr	= hextodec(arg[2]),
		.reg	= hextodec(arg[3]),
		.dir	= arg[4][0],
		.size	= atoi(arg[5]),
		.data	= (__u8 *)malloc(sizeof(__u8)*(atoi(arg[5])+1)),
		};

	if(i2c_rw.dir == 'r'){
		memset(i2c_rw.data,0,sizeof(__u8)*i2c_rw.size);
	}else if (i2c_rw.dir == 'w'){
		if (i2c_rw.size > argc-6){
			printf("few arguments, please check\n");
			goto error1;
		}else{
			int i;
			i2c_rw.data[0]=i2c_rw.reg;
			for (i=1; i <= i2c_rw.size; i++){
			i2c_rw.data[i]=hextodec(arg[5+i]);
			}
		}
	}

	int reval=i2c_handle(i2c_rw.bus_id,i2c_rw.addr,i2c_rw.reg,\
			     i2c_rw.dir,i2c_rw.size,i2c_rw.data);

	if (reval){
		printf("i2c_handle error\n");
		goto error1;
	}
	
	if (i2c_rw.dir == 'r'){
		printf("Register 0x%02x, Read %d bytes.\n", i2c_rw.reg, i2c_rw.size);
		int r;		
		for (r=0; r < i2c_rw.size; r++) {
			printf("0x%02x ",i2c_rw.data[r]);
		}
		printf("\n");
	 } else if(i2c_rw.dir == 'w'){
		printf("Register 0x%02x, Write %d bytes.\n",i2c_rw.reg, i2c_rw.size);
		int w;		
		for (w=1; w <= i2c_rw.size; w++) {
			printf("0x%02x ",i2c_rw.data[w]);
		}
		printf("\n");
	}
error1:
	free(i2c_rw.data);
	return 0;	
}

